package com.hippo.database.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.hippo.database.annotation.Idempotent;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName IdempotentAspect
 * @Description TODO 切面处理重复提交
 * @Author tangxl
 * @create 2023-05-17 09:41
 **/
@Aspect
@Component
public class IdempotentAspect {
    private final RedisTemplate redisTemplate;

    @Autowired
    public IdempotentAspect(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(com.hippo.database.annotation.Idempotent)")
    public  Object idepotent(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        // 获取请求方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解信息
        Idempotent idepotent = method.getAnnotation(Idempotent.class);
        String key = getKey(joinPoint);
        // 判断是否已经请求过一样的
        if(redisTemplate.hasKey(key)){
            throw new RuntimeException("请忽重复提交");
        }
        // 标记已请求
        redisTemplate.opsForValue().set(key,"1",idepotent.expire(), TimeUnit.SECONDS);
        // 处理请求
        return joinPoint.proceed(args);
    }

    /**
     * 获取redis key:类名:方法名:参数1:参数2:...
     * @param joinPoint
     * @return
     */
    private String getKey(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append(className).append(":").append(methodName);
        for(Object arg: args){
            sb.append(":").append(arg.toString());
        }
        return sb.toString();

    }
}
